package com.alexsun.calc

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import soup.neumorphism.NeumorphButton

/**
 * Android 拟态计算器
 * @author Sun-ZhenXing
 *
 * 计算器使用三种状态：
 * - INPUT：输入状态
 * - WAIT：等待下一个数字输入，保持当前数字
 * - RES：结果状态
 *
 * 通过各种状态间的切换来实现各种功能。
 */

/**
 * 自定义转换方法，用于保留精度。不使用科学计数法输出，末尾裁剪多余的 .0
 * 
 * Reference: https://stackoverflow.com/questions/57755536
 */
fun Double.toResultString(): String = toBigDecimal().toPlainString().removeSuffix(".0")

/**
 * 运算符定义
 */
enum class OperatorType {
    NONE, PLUS, MINUS, TIMES, DIV
}

/**
 * 定义有限状态机的状态表
 */
enum class Status {
    INPUT, WAIT, RES
}

class MainActivity : AppCompatActivity(), View.OnClickListener {

    private var opNumA: Double? = null
    private var opNumB: Double? = null
    private var ans: Double? = null
    private var currOperator: OperatorType = OperatorType.NONE
    private var currStatus: Status = Status.INPUT
    private var _textBox: EditText? = null

    private val textBox get() = _textBox!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        _textBox = findViewById(R.id.edittext)
        // 绑定所有按钮的事件监听器
        val btnArray = arrayOf(
            R.id.button_c,
            R.id.button_ce,
            R.id.button_del,
            R.id.button_div,
            R.id.button_times,
            R.id.button_minus,
            R.id.button_plus,
            R.id.button_eq,
            R.id.button_dot,
            R.id.button_sign,
            R.id.button1,
            R.id.button2,
            R.id.button3,
            R.id.button4,
            R.id.button5,
            R.id.button6,
            R.id.button7,
            R.id.button8,
            R.id.button9,
            R.id.button0,
        )
        for (btnID in btnArray) {
            findViewById<NeumorphButton>(btnID).setOnClickListener(this)
        }
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.button_c -> {
                clearAll()
            }
            R.id.button_ce -> {
                textBox.setText("")
            }
            R.id.button_del -> {
                if (textBox.text.isNotEmpty()) {
                    val len = textBox.text.length
                    textBox.setText(textBox.text.toString().subSequence(0, len - 1))
                }
                // 删除时指定为输入模式
                currStatus = Status.INPUT
            }
            R.id.button_sign -> {
                if (textBox.text.isNotEmpty() && textBox.text.toString()[0] == '-') {
                    val len = textBox.text.length
                    textBox.setText(textBox.text.toString().subSequence(1, len))
                } else {
                    val resText = "-" + textBox.text.toString()
                    textBox.setText(resText)
                }
                // 修改符号指定为输入模式
                currStatus = Status.INPUT
            }
            R.id.button_plus -> {
                inputOperator()
                currOperator = OperatorType.PLUS
                updateHint()
            }
            R.id.button_minus -> {
                inputOperator()
                currOperator = OperatorType.MINUS
                updateHint()
            }
            R.id.button_times -> {
                inputOperator()
                currOperator = OperatorType.TIMES
                updateHint()
            }
            R.id.button_div -> {
                inputOperator()
                currOperator = OperatorType.DIV
                updateHint()
            }
            R.id.button_eq -> {
                calculate()
            }
            else -> {
                // 输入数字行为
                val numberButton = v as NeumorphButton
                // 如果是结果状态输入数字，自动清除上一次结果
                if (currStatus == Status.RES) {
                    clearAll()
                }
                val resText = if (currStatus == Status.INPUT) {
                    textBox.text.toString() + numberButton.text
                } else {
                    numberButton.text
                }
                textBox.setText(resText)
                currStatus = Status.INPUT
            }
        }
    }

    /**
     * 输入运算符: A op B
     */
    private fun inputOperator() {
        if (opNumA == null) {
            opNumA = getNumber()
        } else {
            opNumB = getNumber()
            if (opNumB == null) {
                clearAll()
                return
            }
            calculate()
            opNumA = ans
        }
        opNumB = null
        currStatus = Status.WAIT
    }

    /**
     * 更新提示文本
     */
    private fun updateHint() {
        if (opNumA == null) return
        val hintText: TextView = findViewById(R.id.hintText)
        val resText = opNumA!!.toResultString() + when (currOperator) {
            OperatorType.PLUS -> " +"
            OperatorType.MINUS -> " -"
            OperatorType.TIMES -> " ✕"
            OperatorType.DIV -> " ÷"
            OperatorType.NONE -> ""
        }
        hintText.text = resText
    }

    /**
     * 开始计算
     */
    private fun calculate(): Boolean {
        val hintText: TextView = findViewById(R.id.hintText)
        when (currStatus) {
            Status.INPUT -> opNumB = getNumber()
            Status.RES -> opNumA = ans!!
            Status.WAIT -> {
                clearAll()
                Toast.makeText(this@MainActivity, "输入错误！", Toast.LENGTH_SHORT).show()
                return false
            }
        }
        // 如果没有进行运算，直接显示结果
        if (opNumA == null && opNumB != null) {
            ans = opNumB
            textBox.setText(ans!!.toResultString())
            currStatus = Status.RES
            hintText.text = textBox.text
            return true
        }
        // 如果没有完整内容，返回
        if (currOperator == OperatorType.NONE || opNumA == null || opNumB == null) {
            clearAll()
            Toast.makeText(this@MainActivity, "请输入完整", Toast.LENGTH_SHORT).show()
            return false
        }
        // 更新提示文本
        val resText = opNumA!!.toResultString() + when (currOperator) {
            OperatorType.PLUS -> " + "
            OperatorType.MINUS -> " - "
            OperatorType.TIMES -> " ✕ "
            OperatorType.DIV -> " ÷ "
            OperatorType.NONE -> " "
        } + opNumB!!.toResultString() + " ="
        hintText.text = resText

        // 计算结果
        ans = when (currOperator) {
            OperatorType.PLUS -> opNumA!! + opNumB!!
            OperatorType.MINUS -> opNumA!! - opNumB!!
            OperatorType.TIMES -> opNumA!! * opNumB!!
            OperatorType.DIV -> opNumA!! / opNumB!!
            else -> 0.0
        }
        opNumA = null

        // 更新结果显示状态
        textBox.setText(ans!!.toResultString())
        currStatus = Status.RES
        return true
    }

    /**
     * 清除全部信息
     */
    private fun clearAll() {
        opNumA = null
        opNumB = null
        currOperator = OperatorType.NONE
        textBox.setText("")
        val hintText: TextView = findViewById(R.id.hintText)
        hintText.text = ""
    }

    /**
     * 从 textBox 获取数字，如果不合法返回 null
     */
    private fun getNumber(): Double? {
        return try {
            textBox.text.toString().toDouble()
        } catch (e: NumberFormatException) {
            Toast.makeText(this, "输入不合法", Toast.LENGTH_SHORT).show()
            null
        }
    }
}